iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
自我挑戰組

React 個人讀書會系列 第 21

Day 21 - 管理複雜邏輯:useReducer

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20231005/20103817zPncbZ09Iy.jpg

什麼是 useReducer?

useReducer 是 React 提供的 Hooks 之一,用於管理狀態和處理與狀態相關的操作。

它與 useState 不同,useState 主要用於管理單一值的狀態,而 useReducer 更適用於複雜的狀態邏輯,例如物件或陣列。

使用 useReducer 管理狀態

我們將創建一個計數器應用程式,並使用 useReducer 來管理計數器的狀態。

  1. 首先,定義狀態的初始值 countstep
const initialState = {
  count: 0,
  step: 1
};
  1. 創建 Counter 元件的基本外觀。
const initialState = {
  count: 0,
  step: 1
};

function Counter() {
  return (
    <div className="counter">
      <div>
        <input
          type="range"
          min="0"
          max="10"
        />
        <span>{step}</span>
      </div>

      <div>
        <button>-</button>
        <input />
        <button>+</button>
      </div>

      <div>
        <button>Reset</button>
      </div>
    </div>
  );
}
  1. 我們可以在元件內部使用 useReduceruseReducer 這邊傳入兩個參數,第一個參數 reducer 是我們稍後要處理狀態邏輯的一個函式,第二個參數 initialState 是我們狀態的初始值。
const initialState = {
  count: 0,
  step: 1
};

function Counter() {
  useReducer(reducer, initialState);

  return (
    ...
  );
}
  1. useReduceruseState 一樣會返回包含兩個值的陣列,所以在這邊一樣使用陣列解構取出,並將其命名為 statedispatchstate 將會是 countstep 的物件資料,因此我們還可以從 state 再解構出我們的 countstep 狀態。
const initialState = {
  count: 0,
  step: 1
};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;

  return (
    ...
  );
}
  1. 接著將狀態 countstep 綁定到 input 元素上面。
const initialState = {
  count: 0,
  step: 1
};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;

  return (
    <div className="counter">
      <div>
        <input
          type="range"
          min="0"
          max="10"
          value={step}
        />
        <span>{step}</span>
      </div>

      <div>
        <button>-</button>
        <input value={count} />
        <button>+</button>
      </div>

      <div>
        <button>Reset</button>
      </div>
    </div>
  );
}
  1. 新增事件處理函式。
const initialState = {
  count: 0,
  step: 1
};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;
  
  function defineCount() {};

  function defineStep() {};

  function handleDecrement() {};

  function handleIncrement() {};

  function handleReset() {};

  return (
    <div className="counter">
      <div>
        <input
          type="range"
          min="0"
          max="10"
          value={step}
          onChange={defineStep}
        />
        <span>{step}</span>
      </div>

      <div>
        <button onClick={handleDecrement}>-</button>
        <input value={count} onChange={defineCount} />
        <button onClick={handleIncrement}>+</button>
      </div>

      <div>
        <button onClick={handleReset}>Reset</button>
      </div>
    </div>
  );
}
  1. 當我們要修改狀態值的時候,必須透過 dispatch 這個函式去觸發,並且我們可以傳入一個物件去包含我們所需要的資料。
const initialState = {
  count: 0,
  step: 1
};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;

  function defineCount(e) {
    dispatch({
      type: "setCount",
      payload: Number(e.target.value)
    });
  };

  function defineStep(e) {
    dispatch({
      type: "setStep",
      payload: Number(e.target.value)
    });
  };

  function handleDecrement() {
    dispatch({ type: "decrement" });
  };

  function handleIncrement() {
    dispatch({ type: "increment" });
  };

  function handleReset() {
    dispatch({ type: "reset" });
  };

  return (
    <div className="counter">
      <div>
        <input
          type="range"
          min="0"
          max="10"
          value={step}
          onChange={defineStep}
        />
        <span>{step}</span>
      </div>

      <div>
        <button onClick={handleDecrement}>-</button>
        <input value={count} onChange={defineCount} />
        <button onClick={handleIncrement}>+</button>
      </div>

      <div>
        <button onClick={handleReset}>Reset</button>
      </div>
    </div>
  );
}
  1. 觸發 dispatch 函式後,傳入的值將會在 reducer 函式去接收。
const initialState = {
  count: 0,
  step: 1
};

function reducer(state, action) {}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;

  function defineCount(e) {
    dispatch({
      type: "setCount",
      payload: Number(e.target.value)
    });
  };

  function defineStep(e) {
    dispatch({
      type: "setStep",
      payload: Number(e.target.value)
    });
  };

  function handleDecrement() {
    dispatch({ type: "decrement" });
  };

  function handleIncrement() {
    dispatch({ type: "increment" });
  };

  function handleReset() {
    dispatch({ type: "reset" });
  };

  return (
    ...
  );
}
  1. reducer 函式內部,我們可以使用 switch 來判斷 dispatch 傳入的 type 屬性,再來決定要執行哪些操作。
const initialState = {
  count: 0,
  step: 1
};

// state 是目前的狀態
// action 是我們下方 dispatch 傳入的物件資料
function reducer(state, action) {
  switch (action.type) {
    case "decrement":
      return {
        ...state,
        count: state.count - state.step
      };
    case "increment":
      return {
        ...state,
        count: state.count + state.step
      };
    case "setCount":
      return {
        ...state,
        count: action.payload
      };
    case "setStep":
      return {
        ...state,
        step: action.payload
      };
    case "reset":
      return initialState;
    default:
      throw new Error("錯誤的 type");
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;

  function defineCount(e) {
    dispatch({
      type: "setCount",
      payload: Number(e.target.value)
    });
  };

  function defineStep(e) {
    dispatch({
      type: "setStep",
      payload: Number(e.target.value)
    });
  };

  function handleDecrement() {
    dispatch({ type: "decrement" });
  };

  function handleIncrement() {
    dispatch({ type: "increment" });
  };

  function handleReset() {
    dispatch({ type: "reset" });
  };

  return (
    <div className="counter">
      <div>
        <input
          type="range"
          min="0"
          max="10"
          value={step}
          onChange={defineStep}
        />
        <span>{step}</span>
      </div>

      <div>
        <button onClick={handleDecrement}>-</button>
        <input value={count} onChange={defineCount} />
        <button onClick={handleIncrement}>+</button>
      </div>

      <div>
        <button onClick={handleReset}>Reset</button>
      </div>
    </div>
  );
}

useReducer 主要是透過 dispatch 函式來跟 reducer 函式做溝通,在邏輯上會比 useState 複雜一些,但假如我們的狀態是會相互關聯的,就可以嘗試使用 useReducer 來調整程式碼架構。

結語

useReducer 用於處理複雜的狀態邏輯。通過定義 reducer 函式並使用 dispatch 函式,我們可以有效地管理應用程式的狀態和操作。


上一篇
Day 20 - 重返 Class-Based 元件之旅
下一篇
Day 22 - 路由管理:React Router
系列文
React 個人讀書會30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言